from django.shortcuts import render
from django_redis import get_redis_connection
from django import http
from decimal import Decimal
from django.utils import timezone
import json
from django.db import transaction
from django.views import View

from meid_duo.utils.views import LoginRequiredView
from meid_duo.utils.response_code import RETCODE
from users.models import Address
from goods.models import SKU
from .models import OrderGoods,OrderInfo
# Create your views here.
class OrderSettlementView(LoginRequiredView):
    """去结算界面"""
    def get(self,request):
        user = request.user   # 获取当前用户对象
        # 查询当前用户的所有未逻辑删除的收货地址
        address = Address.objects.filter(user=user,is_deleted=False)
        # 三目运算判断，exists()判断查询集中是否有值，
        addresses = address if address.exists() else None

        redis_conn = get_redis_connection('carts')  # 连接redis数据库
        redis_dict = redis_conn.hgetall('carts_%s' % user.id) # 查看当前用户在数据库中存储的哈希数据
        selected_dict = redis_conn.smembers('selected_%s' % user.id) # 查看当前用户在数据库中存储的set集合数据
        # 展示勾选了的商品
        cart_dict = {}
        for sku_id in selected_dict: # 遍历set集合，得到每一个sku_id
            # 在空字典中创建键值对，以skuID为键，通过查询到的哈希数据取到sku_id键对应的值count作为cart_dict的值
            # 当前字典的数据格式为{'sku_id': count}  ==> {2:3}  ==> sku_id为2号的商品购买了3件
            cart_dict[int(sku_id)] = int(redis_dict[sku_id])
        # 查询当前用户所购买商品的详细信息，即得到所有sku组合成的查询集，cart_dict.keys()取到的是当前用户的所有sku_id
        sku_qs = SKU.objects.filter(id__in=cart_dict.keys())
        total_count = 0   # 设置总数量
        total_amount = Decimal("0.00")  # 设置总价
        for sku in sku_qs:  # 遍历查询集得到每个sku
            count = cart_dict[sku.id]   # 当前每个sku购买的数量
            sku.count = count    # sku绑定一个count属性
            sku.amount = count * sku.price    # 获得总价，并绑定到新属性上

            total_count += count   #  数量累加
            total_amount += sku.amount   # 总价累加


        freight = Decimal('10.00')  # 设置运费
        context = {
            'addresses': addresses,  # 用户收货地址
            'skus': sku_qs,  # 勾选的购物车商品数据
            'total_count': total_count,  # 勾选商品总数量
            'total_amount': total_amount,  # 勾选商品总价
            'freight': freight,  # 运费
            'payment_amount': total_amount + freight  # 实付款
        }
        return render(request,'place_order.html',context)


class OrderCommitView(LoginRequiredView):
    """提交订单"""
    def post(self,request):
        # 获取前端请求体中的数据
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')
        # 校验
        if not all([address_id,pay_method]):
            return http.HttpResponseForbidden('缺少参数必传')
        try:
            address = Address.objects.get(id=address_id)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('地址不存在')
        # 三目校验
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM.get('CASH'),OrderInfo.PAY_METHODS_ENUM.get("ALIPAY")]:
            return http.HttpResponseForbidden("支付方式有误")

        user = request.user  # 获取用户
        # 生成唯一订单ID 本地时间加用户ID ，本地时间精确到秒
        order_id = timezone.localtime().strftime("%Y%m%d%H%M%S") + ("%09d" % user.id)
        #  三目判断获得订单状态，待支付还是待发货
        status = (OrderInfo.ORDER_STATUS_ENUM.get("UNPAID")
                if pay_method == OrderInfo.PAY_METHODS_ENUM.get("ALIPAY")
                else OrderInfo.ORDER_STATUS_ENUM.get("UNSEND"))

        with transaction.atomic():   # 创建事务，并开启
            save_id = transaction.savepoint()   # 创建保存点
            try:
                # 创建订单信息模型类
                order=OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal('0.00'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=status
                    )
                # 连接数据库
                redis_conn = get_redis_connection('carts')
                # 获取数据
                redis_dict = redis_conn.hgetall("carts_%s" % user.id)
                selected_dict = redis_conn.smembers('selected_%s' % user.id)
                carts_dict = {}
                for sku_id_bytes in selected_dict:  # 拼接数据新格式，具体说明参考上个类视图（结算页面）
                    carts_dict[int(sku_id_bytes)]=int(redis_dict[sku_id_bytes])

                for sku_id in carts_dict:  # 获得每一个sku_id
                    while True:  # 和下面的continue 与 break 使用
                        sku = SKU.objects.get(id=sku_id) # 查询每个sku_id对应的sku对象
                        buy_count = carts_dict[sku_id]   # 获取该sku_id购买的商品数量
                        origin_stock = sku.stock    # 当前库存
                        origin_sales = sku.sales     # 当前销量

                        if buy_count > origin_stock: # 判断库存
                        # 若不足，则回滚到指定保存点，即开启事务后，事务的特性保证在事务未提交前，数据库不发生任何变化
                            transaction.savepoint_rollback(save_id)
                            return http.JsonResponse({"code":RETCODE.DBERR,"errmsg":"库存不足"})
                        new_stock = origin_stock - buy_count  #  新库存
                        new_sales = origin_sales + buy_count  #  新销量
                        # sku.sales = new_sales    更新sku销量
                        # sku.stock = new_stock     更新sku库存
                        # sku.save()          # 保存

                        # 此处加入乐观锁   ......filter(...).update(....)
                        # 解决资源抢夺问题，保证同一时刻只能执行一次写入，但读取不受限制，故存在脏数据问题
                        # 原理即在写入数据时用上面获取到的数据作为条件再查一次数据库，看是否数据发生变化，若变化，
                        # 则说明同一时刻有其他人在写入，此时result返回0，则下单失败，反之，正常执行
                        # filter(...).update(....)  返回的结果为受影响的行数
                        result = SKU.objects.filter(id=sku_id,stock=origin_stock).update(stock=new_stock,sales=new_sales)
                        if result == 0:
                            continue   # 下单失败，则跳出此次循环，继续下次，即下单失败重新下单，直到下单成功
                        spu = sku.spu   # 获取sku对应的spu对象
                        spu.sales += buy_count   # 修改spu销量
                        spu.save()   # 保存
                        OrderGoods.objects.create(  # 创建订单商品模型
                            order=order,
                            sku=sku,
                            count=buy_count,
                            price=sku.price
                        )

                        order.total_count += buy_count   # 总购买数量
                        order.total_amount += (sku.price * buy_count)  # 总价
                        break  # 下单成功，跳出循环
                order.total_amount += order.freight   # 总价加运费，实付
                order.save()  # 保存
            except Exception:  # try中任何一个地方导致下单出错失败，全部暴力回滚到保存点
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})
            else:  # 成功执行则提交事务，数据库数据写入
                transaction.savepoint_commit(save_id)
        #  下单成功后，购物车已勾选的数据清空
        pl = redis_conn.pipeline()
        pl.hdel('carts_%s' % user.id, *selected_dict)  # 删除勾选的数据
        pl.delete('selected_%s' % user.id)
        pl.execute()  # 管道执行

        return http.JsonResponse({"code": RETCODE.OK, "errmsg": "OK", "order_id": order_id})


class OrderSuccessView(LoginRequiredView):
    """订单提交成功界面展示"""
    def get(self,request):
        # 查询字符串获取数据
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')
        # 校验
        try:
            OrderInfo.objects.get(order_id=order_id, pay_method=pay_method, total_amount=payment_amount)
        except OrderInfo.DoesNotExist:
            return http.HttpResponseForbidden('订单有误')
        # 数据返回
        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }

        return render(request, 'order_success.html', context)
# class OrderSuccessView(LoginRequiredView):
#     """展示提交订单成功界面"""
#
#     def get(self, request):
#
#         # 接收查询参数
#         query_dict = request.GET
#         order_id = query_dict.get('order_id')
#         payment_amount = query_dict.get('payment_amount')
#         pay_method = query_dict.get('pay_method')
#
#         # 校验
#         try:
#             OrderInfo.objects.get(order_id=order_id, pay_method=pay_method, total_amount=payment_amount)
#         except OrderInfo.DoesNotExist:
#             return http.HttpResponseForbidden('订单有误')
#
#
#
#         # 包装要传给模板的数据
#         context = {
#             'order_id': order_id,
#             'payment_amount': payment_amount,
#             'pay_method': pay_method
#         }
#
#         return render(request, 'order_success.html', context)


class OrderCommentView(LoginRequiredView):
    """评价页面"""
    def get(self,request):
        order_id = request.GET.get('order_id')
        try:
            order = OrderInfo.objects.get(order_id=order_id)
        except OrderInfo.DoesNotExist:
            return http.HttpResponseForbidden("订单错误")
        order_goods_qs = order.skus.filter(is_commented=False)
        uncomment_goods_list = []
        for order_goods in order_goods_qs:
            sku = order_goods.sku
            uncomment_goods_list.append({
                'order_id': order_id,
                'sku_id': sku.id,
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),
                'name': sku.name,
                'score':str(order_goods.score),
                'is_comment':str(order_goods.is_commented),
                'comment':order_goods.comment,
                'is_anonymous':str(order_goods.is_anonymous)
            })
        context = {
            "uncomment_goods_list":uncomment_goods_list
        }
        return render(request,'goods_judge.html',context)

    def post(self,request):
        """保存评论"""
        json_dict = json.loads(request.body.decode())
        order_id = json_dict.get('order_id')
        sku_id = json_dict.get('sku_id')
        comment = json_dict.get('comment')
        score = json_dict.get('score')
        is_anonymous = json_dict.get('is_anonymous')

        if not all([order_id,sku_id,comment,score]):
            return http.HttpResponseForbidden("缺少必传参数")
        try:
            order = OrderInfo.objects.get(order_id=order_id,user=request.user,status=OrderInfo.ORDER_STATUS_ENUM['UNCOMMENT'])
        except OrderInfo.DoesNotExist:
            return http.HttpResponseForbidden("订单信息有误")

        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden("订单信息有误")

        if isinstance(is_anonymous,bool) is False:
            return http.HttpResponseForbidden("数据类型错误")

        OrderGoods.objects.filter(sku_id=sku_id,order_id=order_id,is_commented=False).update(
            is_anonymous=is_anonymous,
            score=score,
            comment=comment,
            is_commented=True
        )
        sku.comments += 1
        sku.save()

        sku.spu.comments += 1
        sku.spu.save()

        if OrderGoods.objects.filter(order_id=order_id,is_commented=False).count() == 0:
            order.status = OrderInfo.ORDER_STATUS_ENUM ['FINISHED']
            order.save()

        return http.JsonResponse({'code':RETCODE.OK,'errmsg':"OK"})


class GoodsCommentView(View):
    def get(self,request,sku_id):
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('错误')

        order_goods_qs = OrderGoods.objects.filter(sku_id=sku_id,is_commented=True).order_by("-create_time")
        comments = []
        for order_goods in order_goods_qs:
            username = order_goods.order.user.username
            comments.append({
                'username': (username[0] + '***' + username[-1]) if order_goods.is_anonymous else username,
                'score': order_goods.score,
                'comment': order_goods.comment
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'comment_list': comments})